﻿using System;
using System.Collections.Generic;

using Microsoft.Xna.Framework;

using ZoeEngine.Framework;
using ZoeEngine.Components.Graphics;

namespace ZoeEngine.Components.Controllers
{
    [ComponentDependency(typeof(SpriteRenderComponent))]
    public class AnimationController : EntityComponent
    {
        private Dictionary<string, AnimationData> _anims = new Dictionary<string, AnimationData>();
        private int _deltaFrameTime = 0;
        private int _currentFrameOffset = 0;
        private bool _loopAnimation = false;
        private AnimationData _currentAnimation;

        private SpriteRenderComponent _renderComp = null;

        public override void Initialize()
        {
            base.Initialize();
        }
        public override void Start()
        {
            base.Start();
            _renderComp = this.Owner.Find<SpriteRenderComponent>();
        }
        /// <summary>
        /// Updates the state of the animation. If the frame time of the current animation has
        /// been exceeded, proceeds to next frame of animation.
        /// </summary>
        /// <param name="gameTime">Time-span since last update.</param>
        public override void Update(GameTime gameTime)
        {
            _deltaFrameTime += gameTime.ElapsedGameTime.Milliseconds;
            if (_deltaFrameTime >= _currentAnimation.frameTime)
            {
                _deltaFrameTime = 0;
                _currentFrameOffset++;
                if (_currentFrameOffset + _currentAnimation.firstFrame > _currentAnimation.lastFrame)
                {
                    _currentFrameOffset = _currentFrameOffset - 1;
                    if (_loopAnimation)
                    {
                        _currentFrameOffset = 0;
                    }
                }
            }

            _renderComp.CurrentFrame = _currentAnimation.firstFrame + _currentFrameOffset;
        }
        /// <summary>
        /// Instructs the animation controller to play an animation.
        /// </summary>
        /// <param name="name">Registered name for this animation.</param>
        /// <param name="loop">Whether or not to loop the animation.</param>
        /// <param name="startAt">Frame in animation to advance to before playing.</param>
        /// <remarks>
        /// Note that regardless of the offset give by the startAt parameter, if the animation is
        /// set to loop, it will loop back to the first frame of the animation.
        /// </remarks>
        /// <see cref="Play"/>
        public void PlayAnimation(string name, bool loop = true, int startAt = 0)
        {
            if (_anims.ContainsKey(name))
            {
                _currentAnimation = _anims[name];
                _loopAnimation = loop;
                _currentFrameOffset = 0;
                _deltaFrameTime = 0;
            }
        }

        /// <summary>
        /// Dictionary containing all animations known by this controller.
        /// </summary>
        public Dictionary<string, AnimationData> Animations
        {
            get
            {
                return _anims;
            }
            set
            {
                _anims = value;
            }
        }
    }

    /// <summary>
    /// Encapsulates the data that defines an animation.
    /// </summary>
    public struct AnimationData
    {
        /// <summary>
        /// Frame where animation starts.
        /// </summary>
        public int firstFrame;
        /// <summary>
        /// Frame where animation ends.
        /// </summary>
        public int lastFrame;
        /// <summary>
        /// Time (in milliseconds) between frames.
        /// </summary>
        public int frameTime;
    }
}